package doveclient

import (
	"crypto/md5"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/juju/errors"
	"github.com/zeast/logs"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

const (
	EnvProdyz  = "prodyz"
	EnvProdcl  = "prodcl"
	EnvPubyz   = "pubyz"
	EnvProdtx  = "shuabao-tx-prod"
	EnvProdhw  = "shuabao-hw-prod"
	EnvDevcd   = "devcd"
	EnvDevbj   = "devbj"
	EnvBenchYz = "benchyz"
)

const (
	doveActionGet     = "Get"
	doveActionUpdate  = "Update"
	doveActionPublish = "Publish"
)

var devURLHeader = "http://dove.int.jumeicd.com/Api/Config/"
var prodURLHeader = "http://dove.int.jumei.com/Api/Config/"

type Client struct {
	username string
	secret   string
	env      string
}

func NewClient(username string, secret string, env string) (*Client, error) {
	if env != EnvProdyz && env != EnvDevcd && env != EnvDevbj && env != EnvBenchYz && env != EnvProdcl && env != EnvProdtx && env != EnvProdhw {
		return nil, errors.Errorf("unknown dov env %s", env)
	}

	return &Client{
		username: username,
		secret:   secret,
		env:      env,
	}, nil
}

func (c *Client) GetDoveConfig(cnfName string) ([]byte, error) {
	var cnf []byte
	l, err := c.genURL(doveActionGet, cnfName)
	if err != nil {
		return nil, err
	}

	resp, err := http.Get(l)
	if err != nil {
		return nil, err
	}

	body, err := unmarshalResp(resp.Body)
	if err != nil {
		return cnf, err
	}

	cnf, _ = json.Marshal(body["data"])
	return cnf, nil
}

func (c *Client) UpdateDoveKey(cnfName string, src []byte) error {
	l, err := c.genURL(doveActionUpdate, cnfName)
	if err != nil {
		return err
	}

	content := base64.StdEncoding.EncodeToString(src)
	v := url.Values(make(map[string][]string))
	v.Set("content", content)

	logs.Debugf("post update dovekey url:%s data:%v", l, v)
	resp, err := http.PostForm(l, v)
	if err != nil {
		return err
	}

	_, err = unmarshalResp(resp.Body)
	if err != nil {
		return err
	}

	return nil
}

func (c *Client) PublishDoveKey(cnfName string) error {
	l, err := c.genURL(doveActionPublish, cnfName)
	if err != nil {
		return err
	}

	resp, err := http.Get(l)
	if err != nil {
		return err
	}

	_, err = unmarshalResp(resp.Body)
	if err != nil {
		return err
	}

	return nil
}

func (c *Client) genToken() string {
	checksum := fmt.Sprintf("%x", md5.Sum([]byte(c.secret)))
	return fmt.Sprintf("%x", md5.Sum([]byte(c.username+checksum+strconv.FormatInt(time.Now().Unix(), 10))))
}

func (c *Client) genURL(action, cnfName string) (string, error) {
	var l string
	var p string
	if action == doveActionPublish {
		i := strings.SplitN(cnfName, ".", 2)
		if len(i) < 2 {
			return l, errors.Errorf("config name invalid. %s", cnfName)
		}
		p = i[0]
	}

	var header, tail string
	switch c.env {
	case EnvDevcd, EnvDevbj, EnvBenchYz:
		header = devURLHeader

	case EnvProdyz, EnvProdcl, EnvProdhw, EnvProdtx, EnvPubyz:
		header = prodURLHeader

	default:
		return l, errors.Errorf("unknown dove env %s", c.env)
	}

	switch action {
	case doveActionGet:
		tail = fmt.Sprintf("token=%s&user=%s&env=%s&name=%s", c.genToken(), c.username, c.env, cnfName)

	case doveActionUpdate:
		tail = fmt.Sprintf("token=%s&user=%s&env=%s&name=%s&type=base", c.genToken(), c.username, c.env, cnfName)

	case doveActionPublish:
		tail = fmt.Sprintf("token=%s&user=%s&env=%s&project_name=%s&config_name=%s", c.genToken(), c.username, c.env, p, cnfName)

	default:
		return l, errors.Errorf("unknown dove action %s", action)
	}

	str := header + action + "?" + tail
	return str, nil
}
